package com.summer.content.jobHeadler;


import com.summer.commonException.XueChengException;
import com.summer.content.config.MultipartSupportConfig;
import com.summer.content.dao.CoursePublishMapper;
import com.summer.content.feignClient.EsClient;
import com.summer.content.feignClient.MediaClient;
import com.summer.content.service.CoursePublishService;
import com.summer.dto.CoursePreviewDto;
import com.summer.messagesdk.model.po.MqMessage;
import com.summer.messagesdk.service.MessageProcessAbstract;
import com.summer.messagesdk.service.MqMessageService;
import com.summer.pojo.CoursePublish;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class PublishJobHeadler extends MessageProcessAbstract {

    @Autowired
    private CoursePublishService coursePublishService;

    @Autowired
    private CoursePublishMapper coursePublishMapper;

    @Autowired
    private MediaClient mediaClient;

    @Autowired
    private EsClient esClient;

    // 接收任务调度，任务处理
    @XxlJob("publishCourseProcess")
    public void publishCourseProcess(){
        System.out.println("--------------------开始执行任务-------------------------");
        // 分片参数
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();
        int processors = Runtime.getRuntime().availableProcessors(); // 获取此电脑最大线程数
        log.info("当前分片：{}， 总分片：{}, 启动线程数：{}", shardIndex, shardTotal, processors);
        this.process(shardIndex, shardTotal, "course_publish", processors, 30);

    }


    /**
     *  课程发布任务处理逻辑。
     *      阶段1：课程页面静态化
     *      阶段2：同步数据到redis
     *      阶段3：同步数据到es （最终面向用户的查询接口，因此放在最后，若放在前面可能出现其他阶段还没完成数据就同步到索引库了，导致搜索课程时出现各种异常）
     *  TODO： 此处分布式事务异常处理方案：
     *      原则：出现异常的阶段标记为0（未完成或执行失败）
     *      方案一：处理阶段1、2、3时出现异常统一向上抛出，抛给process方法的try/catch处理。（就是终止当前任务处理）
     *            特点：一遇到异常就终止执行任务，可能阶段1出现异常，2，3还没执行，万一2，3也有异常，就得再次排查
     *      方案二：对于每一个阶段，都进行try/catch处理异常。
     *            特点：任务的每一个阶段都处理，处理一次就知道哪些阶段出现异常
     *      方案n：欢迎补充......
     * @param mqMessage 任务记录
     * @return true:所有阶段执行成功，false：至少有一个阶段执行失败
     */
    @Override
    public boolean execute(MqMessage mqMessage) {
        long courseId = Long.parseLong(mqMessage.getBusinessKey1());
        //1. 阶段1：页面静态化
        generateCourseHtml(mqMessage,courseId);
        //2. 阶段2：将数据写入redis
        //3. 阶段3：将数据写入es
        data2es(mqMessage, courseId);
        //4. 未发生异常，所有阶段都完成，返回true，否则返回false
        return true;
    }

    // 页面静态化、存入minio, 若出现异常则抛出
    private void generateCourseHtml(MqMessage mqMessage,long courseId){
        log.debug("开始进行课程静态化,课程id:{}",courseId);
        //消息id
        Long id = mqMessage.getId();
        //消息处理的service
        MqMessageService mqMessageService = this.getMqMessageService();
        //1. 消息幂等性处理
        int stageOne = mqMessageService.getStageOne(id); // 实时查询，防止两次获取的数据不一致，进而导致重复执行
        if(stageOne >0){
            log.debug("课程静态化已处理直接返回，课程id:{}",courseId);
            return ;
        }
        //2. 课程页面静态化业务逻辑，执行失败则抛出异常
        //2.1 通过页面模板生成静态页面，存入临时文件
        File htmlFile = coursePublishService.generateHtmlToFile(courseId);
        if (htmlFile == null){
            XueChengException.cast("页面静态化失败，courseId = " + courseId);
        }
        //2.2 远程调用media-api将临时文件存入minio
        String objectName = "course/" + courseId + ".html";
        coursePublishService.insert2minio(htmlFile, objectName);

        //3. 保存第一阶段状态，
        mqMessageService.completedStageOne(id);
    }

    // 数据同步到es
    private void data2es(MqMessage mqMessage, long courseId){
        Long id = mqMessage.getId();
        MqMessageService mqMessageService = this.getMqMessageService();
        //1. 幂等性处理
        int stageThree = mqMessageService.getStageThree(id); // 实时查询，防止两次获取的数据不一致，进而导致重复执行
        if(stageThree >0){
            log.debug("课程静态化已处理直接返回，课程id:{}",courseId);
            return ;
        }

        //2. 数据同步, 失败则此方法抛出异常
        CoursePublish coursePublish = coursePublishMapper.selectById(courseId);
        if (coursePublish == null){
            XueChengException.cast("获取发布课程信息失败，courseId = " + courseId);
        }
        boolean insert2es = esClient.insert2es(coursePublish);
        if (!insert2es){
            XueChengException.cast("数据同步到es失败，走了降级逻辑");
        }
        // 3. 完成数据同步，更新状态
        mqMessageService.completedStageThree(id);
    }


}